#! /usr/bin/env python3
import getopt
import sys
import struct
import re
 
 
def symbol_split(ss):
    if "&" in ss:
        cdt = ss.split('&')
    else:
        cdt = [ss]
    return cdt
 
 
def compare_operate(filter, packet):
    result = 1
    for ft in filter:
        ft_item = ft.split()
        offset = int(ft_item[0]) * 2 + 32
        length = int(ft_item[1]) * 2
        s = packet[offset:offset + length]  # original value
        d = int(s, 16) & int(ft_item[2], 16)  # after mask
        
        cdt = symbol_split(ft_item[3])
 
        for i in cdt:
            if (('>' in i) and (d <= int(i[1:], 16))) or (('<' in i) and (d >= int(i[1:], 16))) or \
                    (('==' in i) and (d != int(i[2:], 16))) or (('!=' in i) and (d == int(i[2:], 16))):
                result = 0
 
    return result
 
def range_operate(pct_range, packet_num):
    range_flag = 1
    for r in pct_range:
        if (('>' in r) and (packet_num <= int(r[1:]))) or (('!=' in r) and (packet_num == int(r[2:]))) \
                or (('==' in r) and (packet_num < int(r[2:]))):
            range_flag = 0
            break
        if (('<' in r) and (packet_num >= int(r[1:]))) or (('==' in r) and (packet_num > int(r[2:]))):
            range_flag = 2
            break
    return range_flag
 
 
def decode_pcap(read_path, opt_dict):
    packet_num = 0
    filter_num = 0
    fd_read = open(read_path, 'rb')
 
    if '--save' in opt_dict:
        pcap_head = fd_read.read(24)
        fd_write = open(opt_dict['--save'][0], 'wb')
        fd_write.write(pcap_head)
    else:
        fd_read.seek(24, 0)                         # skip pcap head
 
    while True:
        # one packet
        # packet head write
        packet = ''
        packet_num += 1
        head_lseek = fd_read.seek(8, 1)             # -->8 bytes
        if not head_lseek:
            break
        len_sec = fd_read.read(4)          # caplen
        if not len_sec:              # read fail or zero
            break
        seclen = struct.unpack('i', len_sec)
        caplen = seclen[0] & 0xFFFFFFFF
 
        # range_flag = 1 ===>filter
        # range_flag = 0 ===>less, skip and continue
        # range_flag = 2 ===>more, stop and break
        if '--pcap_range' in opt_dict:
            pct_range = symbol_split(opt_dict['--pcap_range'][0])
            range_flag = range_operate(pct_range, packet_num)
 
            if range_flag == 0:
                skip_lseek = fd_read.seek(caplen + 4, 1)
                if not skip_lseek:
                    break
                continue
            elif range_flag == 2:
                break
            # print("%d come in"%packet_num)
 
        packet_lseek = fd_read.seek(-12, 1)
        if not packet_lseek:
            break
        head_sec = fd_read.read(16)
        if len(head_sec) != 16:
            break
 
        # print(head_sec)
        for k in range(4):
            s = struct.unpack('i', head_sec[k*4:(k+1)*4])
            head = s[0] & 0xFFFFFFFF
            packet += str("%08x" % head)
 
        # packet write
        pkt_sec = fd_read.read(caplen)          #caplen
        read_num = len(pkt_sec)
        if read_num != caplen:
            print("Here Read fail")
            break
        if not pkt_sec:              #read fail or zero
            print("Read Null")
            break
 
        for i in range(0, (caplen+3)//4-1):
            sec_tuple = struct.unpack('!i', pkt_sec[i*4:(i+1)*4])
            sec_four = sec_tuple[0] & 0xFFFFFFFF
            packet += str("%08x" % sec_four)
 
        looptime = caplen % 4
        j = caplen - looptime
        while(looptime):
            one_byte = pkt_sec[j]
            saveData = one_byte & 0xFF
            packet += str("%02x" % saveData)
            j += 1
            looptime -= 1
        # print(len(packet))
 
        filter_item = opt_dict['--filter']
 
        if compare_operate(filter_item, packet):                     # filter success
            check_flag = 1
            filter_num += 1                                  # go check
            # print(packet)
            if '--check' in opt_dict:
                check = opt_dict['--check']
                if not compare_operate(check, packet):
                    check_flag = 0
 
            if '--format' in opt_dict:
                result = opt_dict['--format'][0]
 
                if '&(index)' in result:
                    result = result.replace('&(index)', str(packet_num))
                if '&(filter_index)' in result:
                    result = result.replace('&(filter_index)', str(filter_num))
                if '&(time)' in result:
                    result = result.replace('&(time)', str(int(packet[0:8], 16))+'.'+str(int(packet[8:16], 16)))
                if '&(result)' in result:
                    result = result.replace('&(result)', str(bool(check_flag)))
 
                offset_item = re.findall(r'&[(](.*?)[)]', result, re.S)
                # print(format)
 
                for fmt in offset_item:
                    fmt_item = fmt.split()
                    offset = int(fmt_item[0]) * 2 + 32
                    length = int(fmt_item[1]) * 2
                    result = result.replace('&('+fmt+')', str("%x" % (int(packet[offset:offset + length], 16) & int(fmt_item[2], 16))))
 
                if '--filter_range' in opt_dict:
                    flt_range = symbol_split(opt_dict['--filter_range'][0])
                    flt_range_flag = range_operate(flt_range, filter_num)
                    if flt_range_flag == 1:
                        print(result)
                    elif flt_range_flag == 2:
                        break
                else:
                    print(result)
            else:
                print(str(filter_num)+"  "+str(packet_num)+"  "+str(bool(check_flag)))
 
            if '--save' in opt_dict:
                save_seek = fd_read.seek(-(caplen+16), 1)
                if not save_seek:
                    break
                save_sec = fd_read.read(caplen+16)
                if len(save_sec) != caplen+16:
                    break
                fd_write.write(save_sec)
 
    # print('filter ', filter_num, ' packets')
    fd_read.close()
    if '--save' in opt_dict:
        fd_write.close()
 
 
opt_dict = {}
long_args = ['filter=', 'save=', 'check=', 'format=', "pcap_range=", "filter_range="]
opts, args = getopt.getopt(sys.argv[2:], '', long_args)
 
for opt, val in opts:
    if opt in opt_dict:
        opt_dict[opt].append(val)
    else:
        opt_dict[opt] = [val]
 
 
decode_pcap(sys.argv[1], opt_dict)
 
 
